Die Standardimplementierung von hashCode()
auf HotSpot gibt einen zufälligen Wert zurück und speichert ihn der Objektkopf Dies scheint sich in Java nicht geändert zu haben 8 , wo der Hash-Wert durch einen Aufruf von os::random()
:
Ich frage mich, warum hashCode()
ständig denselben Wert zurückgibt, auch nachdem ich die JVM heruntergefahren habe, die ich versucht habe, indem ich den folgenden einfachen Test durchführte, meinen Rechner neu startete und dann main()
erneut ausführte.
Wie kann die Ausgabe immer gleich sein, wenn hashCode()
tatsächlich os::random()
ist?
java -version
gibt
Hinweis:
Sollte sich jemand fragen was System.out.println(obj);
ist, was obj.toString()
aufruft, wenn das Objekt nicht null ist und etwas wie java.lang.Object@659e0bfd
erzeugt, dann hat das hashCode()
: Der Teil nach @
ist Der Hashcode des Objekts ist hexadezimal (und steht nicht mit dem Speicherort des Objekts im Speicher in Beziehung, Gegenteil zu dem, was die Dokumentation vorschlägt, was zu Missverständnisse ).
Um Ihre Frage zu beantworten, müssen wir zuerst die zweite Frage stellen, "Warum ist os::random()
wurde mit einer festen seed ? "
Wie von @DavidSchwartz vorgeschlagen, ist es sehr nützlich, einen "zufälligen" Zahlengenerator mit einem festen Seed zu haben, da er Ihnen ein willkürliches, aber deterministisches Verhalten gibt. Die JVM-Entwickler können os::random()
aufrufen und wissen immer noch, dass das Verhalten der JVM nicht von externen Faktoren abhängig ist. Dies bedeutet unter anderem, dass JVM-Tests wiederholbar sind. die Verwendung eines "richtig" gesetzten RNG würde es schwierig machen, Fehler in Bezug auf den RNG zu reproduzieren.
Jetzt können wir die ursprüngliche Frage beantworten, umformuliert als "Warum verwendet HotSpot Object.hashCode()
os::random()
?"
Die Antwort auf diese Frage ist wahrscheinlich einfach, weil es einfach ist und es funktioniert. Hash-Codes müssen gut verteilt sein, was ein RNG bietet. Der einfachste und zugänglichste RNG in diesem Bereich der JVM ist os::random()
. Da Object.hashCode()
keine Garantie für die Quelle dieser Werte liefert, spielt es keine Rolle, dass os::random()
gar nicht zufällig ist.
Sie werden feststellen, dass dies nur eine mögliche Hashing-Strategie ist, einige andere sind definiert (und durch die hashCode
global), einschließlich einer, die sie" wahrscheinlich machen ... den Standard in zukünftigen Versionen. "
Letztendlich ist dies nur ein Implementierungsdetail. Es ist einfach nicht notwendig, Object.hashCode()
aggressiver zu randomisieren, und es ist durchaus möglich, dass andere JVMs dies nicht tun oder andere Betriebssysteme sich anders verhalten. Tatsächlich sehe ich in Eclipse verschiedene Hash-Codes, wenn Sie Ihren Code wiederholt ausführen. Darüber hinaus deutet der Vertrag für Object.hashCode()
darauf hin typische JVM-Implementierungen implementieren Object.hashCode()
auf diese Weise überhaupt nicht:
Dies wird normalerweise implementiert, indem die interne Adresse des Objekts in eine ganze Zahl umgewandelt wird
Beachten Sie auch, dass Ihr Test nur überprüft, ob der erste Aufruf an .hashCode()
konsistent ist. In jedem Multithread-Programm können Sie dieses Verhalten nicht erwarten. Es greift auch auf nichts anderes in der JVM zurück, das os::random()
während der Ausführung aufruft, was es jederzeit tun könnte (wenn beispielsweise der Garbage Collector auf os::random()
das Ergebnis von .hashCode()
aufruft, nachdem der erste GC nicht existiert -deterministisch).
Deterministisches Verhalten macht Code leichter zu debuggen, da es repliziert werden kann. Implementierungen neigen dazu, das wo möglich zu wählen. Stellen Sie sich vor, wie schwierig es wäre, einen Komponententest zu replizieren, der aufgrund einer falschen Handhabung einer Hash-Kollision fehlgeschlagen ist (zB nach einer kürzeren Hash-Länge), wenn die Hashwerte jedes Mal anders waren.
Es hat keinen Vorteil, unterschiedliche Werte zwischen den Ausführungen zu haben. In der Tat erhöht es die Chance, Bugs schwer zu reproduzieren.
Wichtig ist, dass die Wahrscheinlichkeit, dass zwei Objekte denselben Hash-Code erhalten, während einer einzelnen Ausführung niedrig ist.
Wenn jemand einen Seed gefunden hat, der zu einer Folge von Werten führt, die eine lange Zeit benötigen, um eine Wiederholung (oder eine lange Sequenz mit sehr wenigen Wiederholungen) zu erzeugen, dann ist es sinnvoll, jedes Mal damit zu beginnen.
Ich habe die Quelle von Hotspot nicht überprüft, um festzustellen, ob es etwas Mühe gab, einen "guten" Samen zu pflücken. Aber das Ziel ist hier eine gute Verbreitung. Zufälligkeit ist nicht erforderlich und Abweichungen von der Ausführung bis zur Ausführung sind im besten Fall nutzlos, im schlimmsten Fall nicht hilfreich.